﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using mozhi.smarterp.AuthorCenter.Permissions.Groups;
using mozhi.smarterp.AuthorCenter.Permissions.Items;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SimpleStateChecking;

namespace mozhi.smarterp.AuthorCenter.Permissions
{
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IPermissionAppService), typeof(PermissionAppService), typeof(CustomPermissionAppService))]
    public class CustomPermissionAppService : PermissionAppService
    {
        private IPermissionGroupAppService _permissionGroupAppService { get; set; }

        private IPermissionGrantRepository _permissionGrantRepository { get; set; }

        public CustomPermissionAppService(IPermissionManager permissionManager, IPermissionDefinitionManager permissionDefinitionManager, IOptions<PermissionManagementOptions> options, 
            ISimpleStateCheckerManager<PermissionDefinition> simpleStateCheckerManager , IPermissionGrantRepository permissionGrantRepository , IPermissionGroupAppService permissionGroupAppService) : base(permissionManager, permissionDefinitionManager, options, simpleStateCheckerManager)
        {
            _permissionGrantRepository = permissionGrantRepository;
            _permissionGroupAppService = permissionGroupAppService;
        }


        [AllowAnonymous]
        public override async Task<GetPermissionListResultDto> GetAsync(string providerName, string providerKey)
        {
            //await CheckProviderPolicy(providerName);

            var result = new GetPermissionListResultDto
            {
                EntityDisplayName = providerKey,
                Groups = new List<Volo.Abp.PermissionManagement.PermissionGroupDto>()
            };

            var multiTenancySide = CurrentTenant.GetMultiTenancySide();

            var permissionGroups = (await _permissionGroupAppService.GetListAsync(new PermissionGroupFilter() { MaxResultCount = 1000, SkipCount = 0 }))?.Items;

            if(permissionGroups==null)
                return result;

            var permissionGrants = await PermissionManager.GetAllAsync(providerName, providerKey);

            if (permissionGrants == null)
                return result;

            var permissionNames = permissionGrants.Select(x => x.Name).ToList();

            permissionGroups = permissionGroups.Where(x => x.Items.Any(y => permissionNames.Contains(y.Code))).ToList();

            foreach (var group in permissionGroups)
            {
                var groupDto = new Volo.Abp.PermissionManagement.PermissionGroupDto
                {
                    Name = group.Code,
                    DisplayName = group.Name,
                    Permissions = new List<PermissionGrantInfoDto>()
                };

                var permissions = group.Items;

                if (permissions == null)
                    continue;

                var grantInfoDtos = new List<PermissionGrantInfoDto>();

                foreach (var permission in permissions)
                {
                    var parentPermission = permissions.FirstOrDefault(x => x.Id == permission.ParentId);

                    var matchPermissionGrant = permissionGrants.FirstOrDefault(x => x.Name == permission.Code && x.IsGranted == true);

                    var grantInfoDto = new PermissionGrantInfoDto
                    {
                        Name = permission.Code,
                        DisplayName = permission.Name,
                        ParentName = parentPermission?.Code,
                        AllowedProviders = matchPermissionGrant?.Providers.Select(x=>x.Name).ToList(),
                        IsGranted = matchPermissionGrant==null ? false : true,
                        GrantedProviders = matchPermissionGrant?.Providers.Select(x => new ProviderInfoDto{ ProviderKey = x.Key , ProviderName=x.Name }).ToList()
                    };

                    grantInfoDtos.Add(grantInfoDto);
                }

                groupDto.Permissions.AddRange(grantInfoDtos);

                if (groupDto.Permissions.Any())
                {
                    result.Groups.Add(groupDto);
                }
            }

            return result;
        }

        [AllowAnonymous]
        public override async Task UpdateAsync(string providerName, string providerKey, UpdatePermissionsDto input)
        {
            //await CheckProviderPolicy(providerName);

            var permissionGrants = await _permissionGrantRepository.GetListAsync(providerName, providerKey);

            foreach (var permissionDto in input.Permissions)
            {
                await PermissionManager.SetAsync(permissionDto.Name, providerName, providerKey, permissionDto.IsGranted);
            }

            var grantNames = input.Permissions.Select(x=> x.Name).ToList();

            var removeItems = permissionGrants.Where(x => !grantNames.Contains(x.Name)).ToList();

            foreach (var removeItem in removeItems)
            {
                await _permissionGrantRepository.DeleteAsync(removeItem.Id);
            }
        }

        #region
        protected override async Task CheckProviderPolicy(string providerName)
        {
            var policyName = Options.ProviderPolicies.GetOrDefault(providerName);
            if (policyName.IsNullOrEmpty())
            {
                throw new AbpException($"No policy defined to get/set permissions for the provider '{providerName}'. Use {nameof(PermissionManagementOptions)} to map the policy.");
            }

            await AuthorizationService.CheckAsync(policyName);
        }
        #endregion
    }
}
